Stable Diffusion勉強会
2022-09-30
サイボウズラボ機械学習勉強会
仕組みの話
使い方とかビジネス上の価値とかの話はしない
目的: 「ブラックボックスを減らす」
最初「ブラックボックスをなくす」と書いていたが、ブラックボックスがなくなるまで深さ優先探索で掘っていったら10時間経っても終わらないので幅優先探索にする
Stable Diffusionの仕組み
ざっくり全体像
https://gyazo.com/f3a87d87da66e6c21724d190130d09a5
3つの主用部品がある
基本的には「ノイズを取り除く仕組み」を繰り返し使うことで画像を生成する
ネットから集めた画像xとそれにノイズを加えたものyを用意して「yを入力したらxが出るニューラルネット」を学習させる
これを純粋なノイズに対して適用すると新しい画像が生まれる
https://gyazo.com/ce8e64d674d1dcb54c5be9e07438e3b2
実際にStable Diffusionの処理中の値を抜き出して可視化した
左端が純粋なノイズ、ノイズ除去を繰り返して猫になってる
https://gyazo.com/f3a87d87da66e6c21724d190130d09a5
このlatentの意味
画像自体の空間ではなく、VAEで低次元な空間(latent space)にエンコードしてから拡散モデルをしてデコードする Stable Diffusionの標準的な設定だと4×64×64次元のテンソル
https://gyazo.com/0ee08cf35c00aa27a9f4cd99c25acc8c
最後にこのVAE decodeで512×512のRGB画像に戻してる
空間が80万次元(3×512×512)から2万次元(4×64×64)に縮んだことで学習が効率的になった
Denoiseのプロセスが10〜100倍高速になった
かつてのモデルでは1ステップずつDenoiseしていた
これを陰解法を使って複数ステップまとめて行なう
「処理Xを20回行った結果」を許容可能な精度で推測Yできるなら、処理Xを20回やるのではなく処理Yをしたらいいよね、という発想
実際、Stable Diffusionの標準的な設定だと1000ステップのノイズ除去処理を、20ステップずつ塊にして50回で行なっている
https://gyazo.com/66f99c801210fb64d6c16c0affdc7786
ここまでで質問
Q: Stableって何を意味してる?
A: 単なるネーミング、DreamBoothのDreamって何と言ってるようなもの、ただのプロジェクト名
Q: ノイズを除去するとか全然安定しそうにない
A: なんでこのネーミングにしたのかは僕は作者ではないのでわからない
B: 安定してきれいな絵が出ますよという雰囲気で名付けたのかな
Q: プロンプトとは
A: 入力する文字列のこと、詳しくは次で説明します
世の中的にはテキストを入れて絵が出てくるツールだと認識されてるけど、ここまでの説明ではまだテキストを入れるところが説明されてない
Q: デノイズの処理自体はプロンプト関係ないってこと?
A: プロンプト関係ないというか…
拡散モデルの画像生成の仕組みとしてはただのノイズからデノイズをして画像を作るというもの
このデノイズの部分を条件付き確率にして条件指定でいじれるようにしてあって、そこのところに条件としてプロンプト文字列を入れることができるようにしている、という仕組み
追記: この条件パラメータは別にテキストである必要などなく、実際に論文では色々な種類の条件を入れる実験をしている
世の中的に「テキストプロンプトを入れるだけで絵が出る!」という経路が知識のない人でも使えてバズっただけのこと
Q: テンソルの4×64×64の4は、4枚あるということ?
A: 4枚あるということ。4チャンネルあると捉えてもいい
Q: 3×512×512の3は?
A: RGBの3チャンネル
Q: 1000ステップを1回にまとめてやらずに20ステップずつ50回やるのはなぜ?
たくさんまとめてやればやるほど「ちゃんと1ステップずつやった時とのズレ」が大きくなる
数式的には1000ステップを1回でやる更新式も作れるけど、これは推定なので誤差も大きくなる
誤差が大きくなりすぎると実用上のメリットがなくなる
時間と精度のトレードオフで、20回くらいだとまあまあ実用的ということ
プロンプト
画像を生成する時に入力するテキストのこと
サブワードに分割された上で、1トークン(多くの場合1単語)が768次元のベクトルになる
bozumanのような辞書にない単語を入れると1単語が3トークンに分割されたりする
プロンプトをトークンに分割したタイミングで77個を超える時は切り捨てて固定長にしてる
短い場合は0でパディング
77トークン×768次元のテンソルになる
https://gyazo.com/9b56949eefcc9f7f031b74a847a68d1b
Stable Diffusionでは学習していない
既存の公開されてるモデルを取ってきて変更しないで部品として使ってるだけ
ざっくりいえば画像と文章の対応づけ(どの画像とどの文章がペアかを当てる)タスクの学習
5つ画像があって5つテキストがあって、どれとどれがペアでしょう?みたいなやつ
画像も文章も768次元のベクトルに射影されてコサイン類似度を計算できる
大規模に学習されてモデルが公開されているのでいろんなプロジェクトが部分部分で活用している
Stable Diffusionではプロンプトの埋め込みにだけ使われている
Denoiseの時にプロンプトの情報を注意機構で見に行く 注意機構が具体的にどんなサイズなのか
ノイズ除去のプロセスは具体的にはU-Netになってて、各層から注意機構が伸びてるんで全体を説明するのは大変だから、一箇所取り出して解説する https://gyazo.com/370a3b55cca24971403d856683e2e1a8
プロンプトは768次元のベクトルが77個ある
それを適当なニューラルネットでそれぞれ300次元に変換したものがある
これがキーの集合Kとバリューの集合V
どっかから取ってきた情報を300次元に変換したもの、これがクエリーQ
QとKをmatmulする、要するにそれぞれのベクトルに対して内積とることに相当する
で、それをSoftmaxに入れるとベクトルの方向が近いところの値が大きくなる
これがアテンションウェイト
仮にどこか1箇所が1になったとするなら次のVとのmatmulは実質的に「その一つを選択する」に相当する
つまり「77トークンのどれに注目して、どれを無視するか」がこの値に込められている
こうやって注目するところだけ混ぜ合わせたバリューを作っている
この仕組み自体はいわゆる「注意機構」であって、ありふれたもの、StableDiffusion特有のことは何もしていない U-Net model for Denoising Diffusion Probabilistic Models (DDPM) https://gyazo.com/3b66a0599134b164ac6317347b8ac6c7
U-Netは簡単にいえば画像を入力として画像を出力する系のタスクに使われる部班
一旦低解像度高チャンネル数の表現を通す仕組み
その細い空間を通す前の情報をコピーして貼り付けている
灰色の矢印と白い四角
低解像度の空間を通した情報と通してない情報の両方を使って高解像度の情報を再現する
この貼り合わせるところで注意機構からの情報も追加していると僕は理解している、間違ってるかもしれない
上記の図では572×572の33万次元の情報が一旦28×28の小さな画像になってる
1024チャンネルもあるので80万次元に増えてる
画像の広い範囲から集めた抽象度の高い情報が、この深いチャンネルのところに詰め込まれているのだろう
高次元の情報の処理で一旦低次元空間を通すってのはオートエンコーダなどとも関連した概念で、それによって認識などに必要ではない「瑣末な情報」を捨てることができる 次元減ってなかった
空間方向の解像度を下げることで瑣末な高周波成分を捨てるなどと言われてる
途中の次元が減ってないのでオートエンコーダ的に元画像の復元タスクをやらせると完璧に復元してしまう
図のネットワークはセグメンテーションのタスクを例に説明されている
そういうタスクなら画像の1ピクセル1ピクセルがバラバラにあっても解けない問題だからたくさんのチャンネルが有効に使われるのかな
セグメントは物理的な局在している
画面上に散らばったセグメントなどはない
=画素の高周波成分が影響しにくい
こういう人間の認知のパターンにフィットした結果を出すために、低解像度化して空間周波数方向の成分を捨てている
guidance_scale
パラメータ一覧にこれがあって「なんだろうな?」と思った人が多そう
https://gyazo.com/74cbce41ccd4ee28594cbf970e64c1e1
プロンプトがない時のノイズの推定(unconditional noise prediction) $ \epsilon_\theta (Z_t) と、ある時のノイズの推定$ \epsilon_\theta (Z_t, c)をそれぞれ計算する
その差に係数をかけて足し合わせる、つまりどれくらいプロンプトを重視するかをこのパラメータで決める
式の形から0〜1にするのかと思いきやデフォルト値は7.5
実際の推定よりもプロンプトの影響を強調して適用している
NovelAIではこの第二項のunconditional noise predictionが、ネガティブプロンプトのconditional noise predictionに置き換わってる。
それによってネガティブプロンプトの効果は通常のプロンプトと逆向きになる
これは未踏ジュニア向けに説明した記事、一旦このページに移って説明する
https://pbs.twimg.com/media/FcruvumacAE1QHu.jpg
プロンプト(テキスト)を入れて画像を生成する仕組みだと世の中的には認識されてるが、プロンプトは即座にベクトル空間に埋め込まれるので加算やスカラー倍ができる
img2imgの仕組み
https://gyazo.com/4ab142483fe6161633e24987b79a5fac
前回text to imageをした時は、初期値は単なる乱数だった
この方法では人間が与えた画像を潜在空間に埋め込んでから、それにちょっとノイズを加えて初期値にする
元画像に例えば75%くらいのノイズをかけて、それから75%の回数のDenoiseをする
ノイズの強さとDenoiseの回数は両方strengthで決まる
ノイズが少なければ元の画像に近いものになる
ノイズを大きくすればより大きく変わる
この解説の絵のパラメータは下記
prompt: "cat, sea, wave, palm tree, in Edvard Munch style"
strength: 0.75
strengthを変えて試してみた結果
strengthを0.6から0.99まで0.01刻みで増やした
https://gyazo.com/0879fa5004d77ded58b9bcc98a34f055
画像の「step: 0.1」は「step: 0.01」のtypo
ノイズの量が多いほど元の絵を無視してフリーダムに描く
左上では僕が書いたものとだいぶ近い
右下では猫がいなくなったり椰子の木が猫になったり猫が砂に埋められたりフリーダムなことになってる
Q: プロンプトはどうなってる?
A: 全部共通でprompt: "cat, sea, wave, palm tree, in Edvard Munch style"
Edvard Munchってのは「ムンクの『叫び』」のムンクです
空の塗り方にムンクみが出てますね
僕が最初に与えた画像は単色でぺったり塗っていたので左上の方ではそれが再現されてるけど、右下に行くほど空の塗りが複雑になっていく
この実験の時に失敗したなーと思うのは、ノイズを乗せる時のランダムシードを固定してないところ
結果のバリエーションが広いのはそのせい
固定して実験した方がその要素を無視できて良かったかもね
右下とか99%ノイズなわけなので、手前が砂で奥が海という構図を維持できてるのがむしろ驚き
0.6台の猫部分の拡大
https://gyazo.com/c97b908051fea30461729f2a81607013 https://gyazo.com/f37628eb413e3693305bb410120bf37chttps://gyazo.com/db1c4e2836e2b3356de771beff24a00bhttps://gyazo.com/6b29646b7ffd43164777c9da0e639aff
僕が適当に描いたのは「それ猫か?」という耳の形状なのだが、まともな耳の形に修正されている、よい
手書きの図をいれてどうなるか実験
手書きの図をきれいに清書してくれると嬉しい
が、今のところ良いプロンプトを見つけてない
https://gyazo.com/6d7c429bf18c8c4241ff5e4f7b712e32 https://gyazo.com/00a5fe2e0f5608e729b044aac2fdf915
9割ノイズを入れて描き直してるのだけど差がわかりにくい
https://gyazo.com/80829926214f2960bea545cf0c121cea https://gyazo.com/6e3838c5ac16740d741015306a39be96
https://gyazo.com/769aec555c457759ce1d87c81849ee5e https://gyazo.com/82734ded31c35b2eb80db9c7c4cc3f8e
この辺がきれいになってるのだけどw
細かすぎてわかる人の方が少ないw
そして逆に文字などが別の文字に化けてる(5がSになったり)
https://gyazo.com/79a5be84b4d3badcc5552083135937d4
手書き風の画像が出てきてる
パスドローなどできっちり真っ直ぐな線や円弧で仕上げた図表が出るといいなぁ
そういうのを出すプロンプトが見つかるといいのだけど…
ファインチューニングでそういうスタイルを学習させる手もある
inpaint
https://gyazo.com/1e683ae1f60758ddd9d6e36391da3608
https://gyazo.com/7bbbd2f36bc287afe03cd0b19e6f9ce9
「マスクしてないところだけを描き直してくれる機能」ではない
それは一般人向けの雑な説明
「マスクされてるところは一切変更しないで、残りの部分だけを境界の辻褄が合うように作る」って仕組みではない
僕も最初はそうだと思い込んでいたが、理解に反する挙動があって詳しく調べたら違うとわかった
https://gyazo.com/8e774b25c948331055c2f27ecb84b864
マスク領域が維持されてないように見えるけど何がいけないのか??(2022/9/12)
神様部分をマスクしている。上下の余白はマスク外。
https://gyazo.com/71e41bc6a8ae03e54fe89c7309098897
神様は元絵のママになり余白には適当に絵が描かれることを期待したがそうはならなかった。
Q: マスク部分が変わるの驚き
A: ですよね、神様部分がめちゃくちゃ書き変わって「僕の使い方とかコードとかがおかしいのかな?」と悩みました
仕組みがわかると当たり前
間で一旦潜在空間を通るので元の画像を維持できるだけの情報量がない
一見マスク部分が維持されてるように見えるベンチ犬のサンプルも、拡大してみるとマスクされたベンチの網目模様が変化しているのがわかる
https://gyazo.com/3555cfd5ad7d03a520c5be77bb59ad93
たまたま人間がベンチの模様には注目していないので気付きにくいだけ
人間の注意が中央の犬と猫に誘導されている
現実的なワークフローは生成後に改めて合成
Photoshopで合成する例
マスク画像自体を使って合成したら境界が目立つのだろうか?
継ぎ目はあるが継ぎ目が近くなるように学習しているのでこのケースではそれほど目立たなかった
この処理は全自動でできる
この処理をした方を好むかしない方を好むかは、人間の個別のユースケース次第
大したコストもかからないし現実的には両方生成してやるのがよいかな
詳しい仕組み説明してなかった
https://gyazo.com/7bbbd2f36bc287afe03cd0b19e6f9ce9
マスク画像の白い部分は「自由に描いて良い」
黒い部分は「自由に書いて良いが適当な係数で元画像とブレンドするよ」
デノイズの過程で何度も元画像とブレンドされる
なので最終的に黒い部分は「元画像にまあまあ近い」になる
この処理はもちろん潜在空間で行なっているので「潜在空間で近い」にすぎない、64x64の解像度
64×64の画像を512×512にVAEで戻した画像は、元画像に一致しはしない
猫と神様の例では、この「戻した画像」が人間の許容できるクオリティにならなかった
なぜならフェンスの柵と違って神様は「前景」であり「人間が注目するところ」だったから
https://gyazo.com/0fc5ddd200ee59aebb028a38c79c09c0
今まで話していたStable Diffusionのtxt2img はテキストから画像を作る仕組み
Stable Diffusionとは別のプロジェクトとして、画像を入力してそれを説明するタスクがある
これを少しいじって「Stable Diffusionに入れると良さそうなプロンプトを作る」をやっているプロジェクト、リポジトリがある
この例では、黄色い目の黒猫、色鉛筆画で、なんとかさんの書いた絵っぽい
ついでに細々とした属性、チョークで描いた絵だとか炭で描いた風だとか、あと謎にCC-BYのライセンスまでつけてきたんだけどw
a painting of a black cat with yellow eyesの部分がそれ
ざっくり仕組み
画像を分割してそれぞれの部分を説明し、それをまとめる
全体としてみたら黒猫だし、拡大してみると黄色い目、という感じで説明文が生成されている
他には「窓枠の上にいる猫」とか「猫が並んでる」など
なので僕は「black cat」としか指示してないのに、僕が指示した以上のデティール解説が出てきている
目が黄色くなったのは乱数シードの影響です
その後で、付け加える要素を候補から総当たりで探索してる
例えば作家名リストには5219人の名前が書かれている
Edvard MunchとかWassily Kandinskyとか、他に思いつかないけど色々書かれてる
ここから総当たりで探索する
くっつけて類似度が一番増えたものを採用していく
知らない画家や画風キーワードを発見する機会になる
画像も文章も768次元のベクトルに射影されてコサイン類似度を計算できる
目的画像に近くなったかどうかをコサイン類似度で見て、近くなるキーワードを発見して組み合わせる仕組み
注: この768次元は、たまたま次元が一致してるけどトークンの埋め込み空間とは別物
もう1段階先の「テキストと画像が同じ768次元空間に埋め込まれる」の空間
BLIPだけでコサイン類似度0.2くらい出る
2次元のイメージでいると「全然似てないやん」となるが、このコサイン類似度は768次元空間で計算されている
768次元なので100万分の1以下
追加の探索によって0.22~0.24になる
注意点
CLIPの768次元ベクトルの空間での類似度を高める探索を行っている
「そのプロンプトを入れた時にその絵が出力される尤度」を高めているわけではない
そこは全然別物、それをやるのはむしろ次で説明するTextual Inversion
Textual Inversion
https://gyazo.com/461f9aa8415fdc0e45e20a4eb9d42dec
5枚程度の画像Aを食わせて1時間待つと768次元のベクトルBが出てくる仕組み
このベクトルはプロンプトのトークン埋め込みと同じ空間
catとkittenの補間とかをやってた空間
これを*などの普段使わないトークンに割り当てて、以降のプロンプトで使えるようにする
詳しい学習方法は掘り下げてないが、ざっくりいえば「Bに埋め込まれる1単語をプロンプトにして画像生成した時にAと似た特徴の画像が生成される確率が高くなるように学習」
当初は猫の柄やボウズマンのデザインが再現されることを期待したのだが、期待過剰だったのでは
そもそも768次元のベクトル1個が出てくるだけ
ボウズマンの柄みたいな「それを表現するコンパクトな語彙」が存在しないものを1単語相当のベクトルで表現するのは無茶
人類が言語で区別しているがユーザがその区別で表現できてない時には有用
たとえば「こんな感じの猫」を「cat」という曖昧な概念でしか表現できてないユーザ
AIを擬人的に説明(メタファー):
AI「これはorange catっぼいな」
https://gyazo.com/883e354ba11391e3f8ce8bdb3257cf09
AI「でもtabbyではない」
https://gyazo.com/0ca53d63ef0734f925cd8f2965a8fa0e
AI「bicolor的なんだけど色が違うな」
https://gyazo.com/b1820f14ebbb89be348714bd3f1e9c0a
AI「orange bicolor!そうそうこんな感じ」
https://gyazo.com/ab518b99313e00366f5913e8386ab50f
AI「これは違う、yellow eyesではなくgreen eyesだ」
https://gyazo.com/baf126ed3b5b5f7623d3b28074422ac8
擬人化しているので言語的な思考に例えたが、実際にはこういう言語的な思考をするのではなく、ベクトルを変えて「近くなったかどうか」を判断する
768次元しかない「1単語の意味の空間」を通る関係上、サイボウズのロゴのような画像的記憶を学習させることができない
猫の柄に対する語彙と、ボウズマン的なものの柄に対する語彙では前者の方が充実している
なので前者の方がやりやすい
And guess what! You can run this on a 16GB colab in less than 15 mins!
https://pbs.twimg.com/media/Fdv2h9TWYAAun3H.jpg
VRAM16GB、15分でファインチューニングできる
Textual Inversionと原理が違って顔のデザインが維持されてる?
今後試してみたいところ
A: ガサっと変わるところはノイズから画像にしていくプロセスの序盤に近いところで分岐していると思う
イメージを口頭で伝えるのは難しいな
https://gyazo.com/e59c5d923638d28ea56abe2ad8fca381
基本的にはプロンプトのベクトルがほとんど変わらないので、入力が同じなら出力もほとんど同じであるはず
ところがそれが分水嶺を跨いだときは、差が拡大して、その結果入力値が離れているのでさらに差が拡大して…という現象が起きる
Q: 注意機構の意味でのアテンションが大きく変わるところで絵が大きく変わるのかと思った
A: この実験に関していうとプロンプトの形は同一でcatの1単語のベクトルだけを滑らかに変更しているから、アテンションが急激に変わって、それがこの振る舞いに出ている、というわけではないのではないかと思う
アテンションの可視化はまだできていないから断言はできない
(補足: Stable Diffusionにおいてアテンションは、色々変えられはするけどデフォルトでは「プロンプトのトークン77個に対するアテンション」なことに注意、画像のどこに注目するか、的なことはしていない)
catとkittenの間を補間しているとはいえ、どっちも名詞なのだから構文上の大きな変化はないはずで、アテンションは大きく変わってないのでは
B: デノイズを50回繰り返す過程で途中で分岐して、そこから結果が大きく変わって、最終的に出てくるものが非連続であるように見えるということだと思う
A: そう、写像自体が線形ではないので、それを何回も重ねた結果を見ると当然非連続な振る舞いをするところはあるよね、そりゃそうだろうな、という感じの雰囲気です
A: バタフライ効果みたいな話です
補足追記:
差が拡大する写像を何度も重ね掛けしていると初期値のごく僅かな差が結果の大きな差につながりうるという話、東京で蝶が羽ばたいた影響でアメリカにハリケーンがくる、という例え
今回のケースでは正確に言えば、初期値は完全に同一で、写像がごくわずかに違う。この場合でも同様に誤差拡大が発生することはあるだろう、たぶんそれが起こってるのだろう、という話
プロンプトの影響が小さかったとしても、それによって山の右側に落ちるのか左側に落ちるのかが変われば、その後のデノイズでは離れる方向に動いていくわけです
https://gyazo.com/5a0218f95bbc047d3e5470bbced29597
流れが山にぶつかって分岐するイメージ
Q: なるほど、それでそれぞれの道でそれっぽくなるように発展していくと
A: そう、それぞれの道でより良くなるように進んだ結果、入力は差がなかったとしても出力が大きく離れたものにたどり着く、それは非線形なので大いにありうること
A: やってみないとわからないね、僕はできるだろうと思っていたが、思った以上に非連続なジャンプが多いなという印象
一応inpaintのマスクで顔の部分だけ消して「catっていったけどここはもっとkitten寄りで」みたいに指示を出して描き直させることは技術的にはできる
だけども制御効きにくいものに対してそういう細かい操作をしようとするよりランダムシードで100個出してそこから選ぶ方が人間の負担が少ないと思う